#include "stdinc.h"
#include <stdio.h>
#include "Main/Logger.h"
#include "StateMirror.h"
#include "StateListener.h"

using namespace DwarfFort;

StateMirror::StateMirror(DWORD address): Loggable("DwarfFort/StateMirror"),
	mStateTop(0),mStateListeners(GS_LAST_STATE),mStateNames(GS_LAST_STATE)
{
	mStateRoot = (State*) address;
	
	mStateNames.insert(mStateNames.begin(),GS_LAST_STATE,std::string("")); 
	fillGameStateMap();
}

StateMirror::~StateMirror()
{
}

void StateMirror::updateState()
{
	char buffer[200];

	if (mStateRoot->vf_table == 0) return;
	if (mStateCopy.vf_table == 0)
	{
		sprintf(buffer,"Setting up root for state: 0x%X, with processing state: %d",mStateRoot->vf_table,mStateRoot->processingState);
		logMessage(buffer);
		mStateCopy.vf_table = mStateRoot->vf_table;
		mStateCopy.processingState = mStateRoot->processingState; 
		mStateTop = &mStateCopy;
		if (mCopiedStates.size())
			mCopiedStates.erase(mCopiedStates.begin());
		mCopiedStates.push_back(&mStateCopy);
		
	}
	if (mStateCopy.processingState != mStateRoot->processingState)
	{
		sprintf(buffer,"WTF? root.processingState changed from %d to %d",mStateCopy.processingState,mStateRoot->processingState);
		logMessage(buffer);
		mStateCopy.processingState = mStateRoot->processingState; 
	}

	std::vector<State *> states;
//	states.resize(mCopiedStates.size());
	State *curr_state = mStateRoot;

	while (curr_state)
	{
		states.push_back(curr_state);
		curr_state = curr_state->nextState;
	}

	std::vector<int> state_map,
					reverse_map;
	
	

	int i,j;
	reverse_map.insert(reverse_map.begin(),mCopiedStates.size(),-1);
	state_map.insert(state_map.begin(),states.size(),-1);
		
	for (i = 0; i <states.size(); ++i)
	{
		
		for (j =0; j <mCopiedStates.size(); ++j)
			if (states[i]->vf_table == mCopiedStates[j]->vf_table // we found the state in the copies
			    && reverse_map[j] == -1)      // and it's not a duplicate !
			{
				reverse_map[j] = i;
				state_map[i] = j;
				break;
			}
	}
	// sizes changed - need to update list
	bool no_change = mCopiedStates.size() == states.size();
	// if sizes the same, check if mapping is the same
	if (no_change)
		for (i = 0; i <states.size(); ++i)
		{
			// if our copy is equal to the original states, the map would contain consecutive integers 
			if (state_map[i] != i)
			{
				no_change = false;
			}
		}
	// no change detected - notify listeners and return
	if (no_change) 
	{
		updateListeners();
		return; 
	}

	// 
	int hole_count = 0;
	// state removals
	curr_state = &mStateCopy;
	for (j =0; j <mCopiedStates.size(); ++j)
	{
		if (reverse_map[j] == -1)
		{
			// handle state removal;
			eGameState state_type = mGameStateMap[mCopiedStates[j]->vf_table];
			notifyStateRemoved(state_type);

			if (state_type == GS_UNKNOWN)
				sprintf(buffer,"State %s(0x%X) removed at level %d",mStateNames[state_type].c_str(),mCopiedStates[j]->vf_table,j);
			else
				sprintf(buffer,"State %s removed at level %d",mStateNames[state_type].c_str(),j);
			logMessage(std::string(buffer));
			// remove the state from the linked list, and delete it
			State *temp_state = curr_state;
			
			curr_state = temp_state->prevState;
			curr_state->nextState = temp_state->nextState;
			
			if (curr_state->nextState) 
				curr_state->nextState->prevState = curr_state;
			temp_state->nextState = 0;
			delete temp_state;
			hole_count++;
		}
		/*else
		if (reverse_map[j] != (j-hole_count)) // if the state has been moved explicitly - not via removal of other states
		{
			// dunno what to do here
			_asm int 3;
		}*/
		curr_state=curr_state->nextState; 
	}
	hole_count = 0;

	curr_state = &mStateCopy;
	State *prev_state = 0; // should crash if the state_copy is not equal to state root - this one is static (i think :/)
	for (i = 0; i <states.size(); ++i)
	{
		if (state_map[i] == -1)
		{
			// a state has been added
			eGameState state_type = mGameStateMap[states[i]->vf_table];
			notifyStateAdded(state_type);
			if (state_type == GS_UNKNOWN)
				sprintf(buffer,"State %s(0x%X) added at level %d",mStateNames[state_type].c_str(),states[i]->vf_table,i);
			else
				sprintf(buffer,"State %s added at level %d",mStateNames[state_type].c_str(),i);
			logMessage(std::string(buffer));

			State *temp_state = curr_state; 
			
			curr_state = new State();
			curr_state->vf_table = states[i]->vf_table;
			curr_state->processingState = states[i]->processingState;
			prev_state->nextState = curr_state;
			
			curr_state->prevState = prev_state;
			curr_state->nextState = temp_state;
			if (temp_state) temp_state->prevState = curr_state;
			hole_count++;
		}
		/*else
		if (state_map[i] != (i+hole_count)) // if the state has been moved explicitly - not via removal of other states
		{
			// dunno what to do here
			_asm int 3;
		}*/
		prev_state = curr_state;
		curr_state=curr_state->nextState; 
	}
	
	// setup the new list
	mCopiedStates.erase(mCopiedStates.begin(),mCopiedStates.end());
	curr_state = &mStateCopy;
	while (curr_state)
	{
		mCopiedStates.push_back(curr_state);
		curr_state = curr_state->nextState;
	}

	return;


}


void StateMirror::fillGameStateMap()
{
	std::map<DWORD,eGameState> &gsm = mGameStateMap;

	gsm[0x9803D0] = GS_ROOT_STATE;
	gsm[0x994494] = GS_MAIN_MENU;
	gsm[0x9A7E54] = GS_LOAD_GAME;
	gsm[0x98452C] = GS_INGAME;

	mStateNames.at(GS_UNKNOWN) = "GS_UNKNOWN";
	mStateNames.at(GS_ROOT_STATE) = "GS_ROOT_STATE";
	mStateNames.at(GS_MAIN_MENU) = "GS_MAIN_MENU";
	mStateNames.at(GS_LOAD_GAME) = "GS_LOAD_GAME";
	mStateNames.at(GS_INGAME) = "GS_INGAME";
	mStateNames.at(GS_LAST_STATE) = "GS_LAST_STATE";
}


void StateMirror::registerStateListener(DwarfFort::StateListener *listener, DwarfFort::eGameState gameState)
{
	
	std::vector<StateListener *> &listeners = mStateListeners[gameState];

	listeners.push_back(listener);

}

void StateMirror::unRegisterStateListener(DwarfFort::StateListener *listener, DwarfFort::eGameState gameState)
{
	std::vector<StateListener *> &listeners = mStateListeners[gameState];
	
	std::vector<StateListener *>::iterator i;
	for (i = listeners.begin(); i != listeners.end(); ++i)
		if ((*i) == listener)
		{
			listeners.erase(i);
			return;
		}
}


void StateMirror::updateListeners()
{
	int i = 0;
	for (i = 0; i < mCopiedStates.size(); ++i)
	{
		eGameState state_type = mGameStateMap[mCopiedStates[i]->vf_table];
		if (state_type == GS_UNKNOWN) continue;
		std::vector<StateListener *> &listeners = mStateListeners[state_type];
		std::vector<StateListener *>::iterator listener;
		for (listener = listeners.begin(); listener != listeners.end(); ++listener)
			(*listener)->stateUpdate(-i);
	}

}
void StateMirror::notifyStateRemoved(eGameState state)
{
	if (state == GS_UNKNOWN) return;
	std::vector<StateListener *> &listeners = mStateListeners[state];
	std::vector<StateListener *>::iterator listener;
	for (listener = listeners.begin(); listener != listeners.end(); ++listener)
		(*listener)->stateRemoved();

}
void StateMirror::notifyStateAdded(eGameState state)
{
	
	if (state == GS_UNKNOWN) return;
	std::vector<StateListener *> &listeners = mStateListeners[state];
	std::vector<StateListener *>::iterator listener;
	for (listener = listeners.begin(); listener != listeners.end(); ++listener)
		(*listener)->stateCreated();

}